#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pthread.h>
// 在文件顶部添加信号量头文件
#include <semaphore.h>
#include <string.h>
#include "font.h"
 

// 这里是数据的区域
#define MAX_FILENAME_LENGTH 256
// 定义链表节点结构体及Node类型
typedef struct node 
{
    char *filename;
    struct node *next;
} *Node;

void list_pictures(Node head);
void load_pictures_from_file(Node *head, const char *filename);
void destroy_list(Node head);
Node create_node(const char *filename);






// BMP格式头规范
struct bitmap_header
{
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
//_attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐
struct bitmap_info
{
	int32_t size;   // 本结构大小	
	int32_t width;  // 图像宽
	int32_t height; // 图像高
	int16_t planes;
 
	int16_t bit_count; // 色深
	int32_t compression;
	int32_t size_img; // bmp数据大小，必须是4的整数倍
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
}__attribute__((packed));
 
// 以下结构体不一定存在于BMP文件中，除非：
// bitmap_info.compression为真
struct rgb_quad
{
	int8_t blue;
	int8_t green;
	int8_t red;
	int8_t reserved;
}__attribute__((packed));

//触摸屏文件
#define TS_PATH  "/dev/input/event0"
//屏幕文件
#define FB_FILE  "/dev/fb0"

//触摸屏文件
int ts_fd;
//屏幕文件
int lcd_fd;

//映射文件指针
unsigned int *mem_p;
//坐标点
int X , Y ;





volatile int thread_exit_flag = 0;//线程结束和开始标志
volatile int n = 0; // 状态变量，用于控制程序流程   主线程
volatile int continueDisplay = 1; // 标志变量，用于控制循环  主线程

int lcd_init(void);
int lcd_uninit(void);
int show_bmp(const char *pathname);
int get_xy(void);
void show_one(void);
void show_password(void);
int show_bmp_xy(const char *pathname, int start_x, int start_y);
void *pthread_fun1(void *arg);
void *pthread_fun2(void *arg);
char* getNowtime(void) ;
void *pthread_fun3(void *arg);
struct LcdDevice *init_lcd(const char *device);
void show_mplayer(void);
void show_location(void);
void show_weather(void);
void play_video(const char* filename,int *kill_flag) ;
void password(void);


int main(void)
{
    pthread_t thread1,thread2,thread3,thread4;
    
    while (1) 
    {
        // 根据状态变量n来决定执行的流程
        switch (n) 
        {
            case 0:
                // 显示主页面
                show_one(); 
                while (continueDisplay)
                {
                    if(pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
                    {
                        perror("fail to pthread_create");
                    }
                    if(pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
                    {
                        perror("fail to pthread_create");
                    }
                    if(pthread_create(&thread3, NULL, pthread_fun3, NULL) != 0)
                    {
                        perror("fail to pthread_create");
                    }
                    show_location();
                    show_weather();
                    while (1)
                    {
                        get_xy(); // 获取触摸坐标   检测是否触摸了管理员登录区域
                        if (Y < 100 && X > 650) 
                        {
                            thread_exit_flag = 1; // 设置退出标志
                            pthread_join(thread1, NULL); // 清理线程资源
                            pthread_join(thread2, NULL); // 清理线程资源
                            pthread_join(thread3, NULL); // 清理线程资源
                            continueDisplay = 0; // 设置标志变量，退出循环
                            break;
                        }
                    }
                }
                n = 1; // 状态变更为1
                break;
            case 1:
                show_password();
                n = 2; // 状态变更为2
                break;
            case 2:
                password();   //执行密码的逻辑
                break;
            case 3:
                lcd_init();
                show_bmp("user.bmp");
                lcd_uninit();
                get_xy(); // 检测是否触摸了返回按钮  返回主页面
                if (X > 400 && Y > 240) 
                {
                    n = 0; //状态变更为0 
                    thread_exit_flag = 0; // 设置启动标志
                    continueDisplay = 1;
                    break;
                }
                break;
        }
    }
}

/** 
 * 初始化Lcd
 * 
**/
struct LcdDevice *init_lcd(const char *device)
{
	//申请空间
	struct LcdDevice* lcd = malloc(sizeof(struct LcdDevice));
	if(lcd == NULL)
	{
		return NULL;
	} 

	//1打开设备
	lcd->fd = open(device, O_RDWR);
	if(lcd->fd < 0)
	{
		perror("open lcd fail");
		free(lcd);
		return NULL;
	}
	
	//映射
	lcd->mp = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd->fd,0);

	return lcd;
}

/** 
 * 线程执行图片循环
 * 
**/
// void *pthread_fun1(void *arg)
// {
//     const char *filename = "pictures.txt";
//     Node head = NULL; // 初始化头节点为NULL
//     // 从文件加载图片名称
//     printf("从文件加载图片名称...\n");
//     load_pictures_from_file(&head, filename);

//    while (!thread_exit_flag)
//    {
//         show_bmp_xy(head->next->filename,15,150);
//         sleep(3);
//         head = head->next;
//    }

// }

/** 
 * 线程执行广告词语循环
 * 
**/
void *pthread_fun2(void *arg)
{   
    const char *filename = "gg.txt";
    // 初始化头节点为NULL
    Node head = NULL; 
    // 从文件加载图片名称
    printf("从文件加载图片名称...\n");
    load_pictures_from_file(&head, filename);
    
    //将字体写到点阵图上
    int i = 0;
    while (!thread_exit_flag)
    {
        //初始化Lcd
        struct LcdDevice* lcd = init_lcd("/dev/fb0");
                
        //打开字体	
        font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
        
        //字体大小的设置
        fontSetSize(f,50);
            
        //创建一个画板（点阵图）
        bitmap *bm = createBitmapWithInit(570,50,4,getColor(0,255,255,255)); 

        fontPrint(f,bm,0,0,head->filename,getColor(0,255,0,0),0);
        head = head->next;
        
        //把字体框输出到LCD屏幕上
        show_font_to_lcd(lcd->mp,23,35,bm);
        
        //关闭字体，关闭画板
        fontUnload(f);
        destroyBitmap(bm);
        sleep(2); // 等待2秒
    }
    
}

/** 
 * 线程执行视频循环
 * 
**/
void *pthread_fun1(void *arg)
{
    while (!thread_exit_flag)
    {
        show_mplayer();
        //1ms  线程当中至少保证有1ms延时，保证线程会交出CPU资源
        sleep(2);
    }     
}


/** 
 * 播放视频
 * 
**/
void show_mplayer(void)
{
    const char *filename = "mp4.txt";
    Node head = NULL; // 初始化头节点为NULL
    // 从文件加载图片名称
    printf("从文件加载图片名称...\n");
    load_pictures_from_file(&head, filename);
    FILE *fp;
    char command[256];

    // 创建管道
    system("mkfifo pipe");

    // 构建 mplayer 命令
    snprintf(command, sizeof(command), "mplayer -slave -quiet -input file=pipe -geometry 15:150 -zoom -x 600 -y 300 %s", head->filename);

    // 执行命令
    fp = popen(command, "w");
    if (fp == NULL) {
        perror("popen");
        exit(EXIT_FAILURE);
    }

    // 等待命令执行完成
    pclose(fp);

    // 删除管道
    system("rm pipe");
}

/** 
 * 线程执行时间
 * 
**/
void *pthread_fun3(void *arg)
{
    while (!thread_exit_flag)
    {
        //获取系统时间
        char *current_time_str = getNowtime();
        
        // printf("\nCurrent time: %s\n\n", current_time_str);

        //初始化Lcd
        struct LcdDevice* lcd = init_lcd("/dev/fb0");
                
        //打开字体	
        font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
        
        //字体大小的设置
        fontSetSize(f,20);
            
        //创建一个画板（点阵图）
        bitmap *bm = createBitmapWithInit(138,30,4,getColor(0,255,255,255)); 
        //也可使用createBitmapWithInit函数，改变画板颜色
        // bitmap *bm = createBitmap(288, 100, 4);

        fontPrint(f,bm,0,0,current_time_str,getColor(0,255,0,0),0);
        
        //把字体框输出到LCD屏幕上
        show_font_to_lcd(lcd->mp,648,204,bm);
        
        //关闭字体，关闭画板
        fontUnload(f);
        destroyBitmap(bm);
        // 释放由getNowtime分配的内存
        free(current_time_str);
        sleep(1); // 休眠1秒
    }
    
    
}

/** 
 * 位置
 * 
**/
void show_location(void)
{
    char buff[] = {"广州市从化区"};

    //初始化Lcd
    struct LcdDevice* lcd = init_lcd("/dev/fb0");
                
    //打开字体	
    font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
        
    //字体大小的设置
    fontSetSize(f,25);
            
    //创建一个画板（点阵图）
    bitmap *bm = createBitmapWithInit(120,30,4,getColor(0,255,255,255)); 
        //也可使用createBitmapWithInit函数，改变画板颜色
        // bitmap *bm = createBitmap(288, 100, 4);

    fontPrint(f,bm,0,0,buff,getColor(0,255,0,0),0);

    //把字体框输出到LCD屏幕上
    show_font_to_lcd(lcd->mp,658,312,bm);
        
    //关闭字体，关闭画板
    fontUnload(f);
    destroyBitmap(bm); 
}

/** 
 * 天气
 * 
**/
void show_weather(void)
{
    char buff[] = {"多云 25-34 摄氏度"};

    //初始化Lcd
    struct LcdDevice* lcd = init_lcd("/dev/fb0");
                
    //打开字体	
    font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
        
    //字体大小的设置
    fontSetSize(f,20);
            
    //创建一个画板（点阵图）
    bitmap *bm = createBitmapWithInit(120,30,4,getColor(0,255,255,255)); 
        //也可使用createBitmapWithInit函数，改变画板颜色
        // bitmap *bm = createBitmap(288, 100, 4);

    fontPrint(f,bm,0,0,buff,getColor(0,255,0,0),0);

    //把字体框输出到LCD屏幕上
    show_font_to_lcd(lcd->mp,656,428,bm);
        
    //关闭字体，关闭画板
    fontUnload(f);
    destroyBitmap(bm); 
}

/** 
 * 主页面
 * 
**/
void show_one()
{
    lcd_init();
    show_bmp("theme.bmp");
}

/** 
 * 密码页面
 * 
**/
void show_password(void)
{
    lcd_init();
    show_bmp("password.bmp");
    lcd_uninit();
    char buff[] = {"* * * * * *"};

    //初始化Lcd
    struct LcdDevice* lcd = init_lcd("/dev/fb0");
                
    //打开字体	
    font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf");
        
    //字体大小的设置
    fontSetSize(f,60);
            
    //创建一个画板（点阵图）
    bitmap *bm = createBitmapWithInit(300,30,4,getColor(0,255,255,255)); 
        //也可使用createBitmapWithInit函数，改变画板颜色
        // bitmap *bm = createBitmap(288, 100, 4);

    fontPrint(f,bm,0,0,buff,getColor(0,255,0,0),0);

    //把字体框输出到LCD屏幕上
    show_font_to_lcd(lcd->mp,30,105,bm);
        
    //关闭字体，关闭画板
    fontUnload(f);
    destroyBitmap(bm); 
}

/** 
 * 获取系统时间
 * 函数返回当前时间的动态分配字符串
**/
char* getNowtime(void) 
{
    time_t current_time;
    struct tm* now_time;
    char *cur_time = (char *)malloc(21 * sizeof(char)); // 分配足够的内存

    if (cur_time == NULL) 
    {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }

    time(&current_time);
    now_time = localtime(&current_time);

    // 使用strftime填充日期和时间字符串
    strftime(cur_time, 21, "%F %T", now_time); // 直接格式化为 "YYYY-MM-DD HH:MM:SS"

    return cur_time;
}

/** 
 * 初始化屏幕文件
 * 
**/
int lcd_init(void)
{
    //打开frame buffer设备文件--屏幕文件
    lcd_fd = open(FB_FILE, O_RDWR);
    if(lcd_fd == -1)
    {
        perror("open error:");
        return -1;
    }

    //屏幕映射
    mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    if(mem_p == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }

    return 0;
}

/** 
 * 映射内存
 * 
**/
int lcd_uninit(void)
{
   // 解除映射
    if (munmap(mem_p, 800*480*4) == -1) 
    {
        perror("munmap error:");
        return -1;
    }
    close(lcd_fd);
    return 0;
}

/** 
 * 显示一张800×480的24位bmp图片，并将图片放入内存映射空间
 * 
**/
int show_bmp(const char *pathname)
{
    int bmp_fd, i,j;
    //存储bmp文件的像素点
    unsigned char bmp_buff[800*480*3] = {0};
    //写进内存映射缓冲区的缓冲区
    unsigned int *buff = malloc(800*480*sizeof(unsigned int)); // 使用动态分配的内存

    if (buff == NULL) 
    {
        perror("malloc error:");
        return -1;
    }

    bmp_fd = open(pathname, O_RDONLY);
    if(bmp_fd == -1)
    {
        perror("open");
        free(buff);
        return -1;
    }

    //跳过54个字节头
    lseek(bmp_fd, 54, SEEK_SET);

    //读图片的全部RGB到缓冲区
    if (read(bmp_fd, bmp_buff, sizeof(bmp_buff)) != sizeof(bmp_buff)) 
    {
        perror("read error:");
        free(buff);
        close(bmp_fd);
        return -1;
    }

    //将RGB转换为BGR
    for(i=0; i<800*480; i++)
    {
        buff[i] = (bmp_buff[3*i+2] << 16) | (bmp_buff[3*i+1] << 8) | bmp_buff[3*i+0];//AGRB
    }

    // 像素点填充到屏幕
    for(i = 0; i < 480; i++) // 按行遍历
    { 
        for(j = 0; j < 800; j++) // 按列遍历
        { 
            // 从buff中获取像素数据，确保使用正确的索引
            unsigned int pixel = buff[i * 800 + j]; //i=0 :0 - 799
            
            // 将像素数据复制到帧缓冲区mem_p中
            // 确保使用正确的索引，这里假设mem_p的布局与buff相同
            mem_p[((479 - i) * 800) + j] = pixel;  //479*800 - 479*800+799
        }
    }

    free(buff);
    close(bmp_fd);
    return 0;
}

/** 
 * 获取X Y坐标值
 * 
**/
int get_xy(void)
{
    struct input_event ts;

    ts_fd = open(TS_PATH, O_RDWR);
    if(ts_fd == -1)
    {
        printf("open ts failure\n");
        return -1;
    }

    while(1)
    {
        //松开触摸后，再打印
        while (1)
        {
            read(ts_fd, &ts, sizeof(struct input_event));

            //判断类型
            if(ts.type == EV_ABS && ts.code == ABS_X)
            {
                X = ts.value;
            }

            if(ts.type == EV_ABS && ts.code == ABS_Y)
            {
                Y = ts.value;
            }

            //判断按下
            if(ts.type == EV_KEY && ts.code ==  BTN_TOUCH && ts.value == 1){ }
           
            //判断是否松开
            if(ts.type == EV_KEY && ts.code ==  BTN_TOUCH && ts.value == 0)
            {                                                  	             	
                X = X*(800.0/1024.0);      
                Y = Y*(480.0/600.0);
                break;
            }           
        }
        printf("X:%d, Y:%d\n", X, Y);
        return 0;
    }
}

/**
 * 在指定位置显示图片
 */
int show_bmp_xy(const char *pathname, int start_x, int start_y)
{
    int bmp_fd, x, y, i, j;
 
	// 读取BMP格式头，获取图片信息
	struct bitmap_header header;
	struct bitmap_info info;
	struct rgb_quad quad;
 
    bmp_fd = open(pathname, O_RDWR);
    if(bmp_fd == -1)
    {
        printf("open bmp fail\n");
        return -1;
    }   
 
 
    // 第一、二个结构体是必有信息
	read(bmp_fd, &header, sizeof(header));
	read(bmp_fd, &info, sizeof(info));
 
    // 第三个结构体是可选的，需要判断是否存在
	if(info.compression != 0)
	{
		read(bmp_fd, &quad, sizeof(quad));
		fprintf(stderr, "read quad! \n");
	}
 
    // 输出图片相关信息
    printf("图片大小: %d\n", header.size);
    printf("图片尺寸: %d×%d\n", info.width, info.height);
    printf("色深: %dbpp\n", info.bit_count);
 
    int width = info.width;
    int height = info.height;
 
    if((start_x+width) > 800  || (start_y+height) > 480)
    {
        printf("图片越界,无法显示\n");
        close(bmp_fd);
        return -1;
    }
 
    //定义存储图片所有RGB数据的缓冲区
    unsigned char *bmp_buff = (unsigned char *)malloc(width*height*3);
    unsigned int *buff = (unsigned int *)malloc(width*height*4);
 
    //将图片的像素存放bmp_buf当
    read(bmp_fd, bmp_buff, width*height*3);
 
    for(i=0; i<width*height; i++)
    {
        buff[i] = bmp_buff[3*i+0]|bmp_buff[3*i+1]<<8|bmp_buff[3*i+2]<<16;
    }   
 
    //像素点填充到屏幕
    for(y=0; y<height; y++)
    {
        for(x=0; x<width; x++) 
        {
            mem_p[(start_y+y)*800+start_x+x] = buff[(height-1-y)*width+x];
        }
    }

 
    free(bmp_buff);
    free(buff);
    close(bmp_fd);
}

/**
 * 密码的输入与验证
 */
void password(void)
{
    char buff[7] = {1,2,3,4,5,6};
    char buff_password[7] = {0};
    int i = 0;
    buff[6] = '\0';
    buff_password[6] = '\0'; // 添加字符串结束符
    while (1)
    {
        get_xy();
        while (1)
        {
            if (Y < 100 && X < 100) 
            {
                n = 0; // 返回主页面
                thread_exit_flag = 0; // 设置启动标志
                continueDisplay = 1;
                return;
            }
            if ( i <= 5 && Y > 165)
            {
                if (Y < 240 && Y >165 && X < 267) 
                {
                    printf("1\n");
                    buff_password[i] = 1;
                    i++;
                }
                if (Y > 240 && Y < 320 && X < 267) 
                {
                    printf("4\n");
                    buff_password[i] = 4;
                    i++;
                }
                if (Y <400 && Y > 320 && X < 267) 
                {
                    printf("7\n");
                    buff_password[i] = 7;
                    i++;
                }

                if (Y < 240 && Y >165 && X > 267 && X < 530) 
                {
                    printf("2\n");
                    buff_password[i] = 2;
                    i++;
                }
                if (Y > 240 && Y < 320 &&  X > 267 && X < 530)
                {
                    printf("5\n");
                    buff_password[i] = 5;
                    i++;
                }
                if (Y <400 && Y > 320 &&  X > 267 && X < 530)
                {
                    printf("8\n");
                    buff_password[i] = 8;
                    i++;
                }
                if (X > 267 && X < 530 && Y > 400)
                {
                    printf("0\n");
                    buff_password[i] = 0;
                    i++;
                }

                if (Y < 240 && Y >165 &&  X > 530) 
                {
                    printf("3\n");
                    buff_password[i] = 3;
                    i++;
                }
                if (Y > 240 && Y < 320 && X > 530)
                {
                    printf("6\n");
                    buff_password[i] = 6;
                    i++;
                }
                if (Y < 400 && Y > 320 && X > 530)
                {
                    printf("9\n");
                    buff_password[i] = 9;
                    i++;
                }
                if (X > 530 && Y > 400 )
                {
                    //回退
                    printf("i--\n");
                    i--;
                }
                if (X < 267 && Y > 400 )
                {
                    i = 0;
                    printf("null\n");
                }
                
                
                if (strcmp(buff,buff_password) == 0)
                {
                    printf("Password correct\n");
                    n = 3;
                    return;
                }
            }
            else
            {
                printf("password error\n");
                i = 0;
                memset(buff_password, 0, sizeof(buff_password)); // 重置
            }
            break;
        }
    }
}



//下面的是数据结构的代码
// 创建新节点的函数
Node create_node(const char *filename) 
{
    Node new_node = (Node)malloc(sizeof(struct node));
    if (new_node != NULL) {
        new_node->filename = (char *)malloc(strlen(filename) + 1);
        if (new_node->filename != NULL) 
        {
            strcpy(new_node->filename, filename);
        } 
        else 
        {
            perror("内存分配失败");
            free(new_node);  // 如果分配filename失败，释放new_node
            new_node = NULL;
        }
        new_node->next = NULL;
    }
    return new_node;
}

// 销毁循环链表的函数
void destroy_list(Node head) 
{
    if (head == NULL) 
    {
        return; // 如果头节点为空，直接返回
    }

    Node current = head->next; // 从头节点的下一个节点开始
    if (current == NULL) 
    {
        free(head); // 如果没有数据节点，释放头节点
        return;
    }

    // 使用while循环遍历并释放链表节点
    while (current->next != head) 
    {
        Node next = current->next; // 保存下一个节点的地址
        free(current->filename);   // 释放当前节点的filename
        free(current);             // 释放当前节点
        current = next;            // 移动到下一个节点
    }

    // 释放循环链表的最后一个节点和头节点
    free(current->filename); // 释放最后一个节点的filename
    free(current);           // 释放最后一个节点
    free(head);              // 释放头节点
}

// 从文件中加载图片名称到循环链表
void load_pictures_from_file(Node *head, const char *filename) 
{
    FILE *file = fopen(filename, "r");
    if (!file) 
    {
        perror("无法打开文件");
        return;
    }

    char buffer[MAX_FILENAME_LENGTH];
    Node tail = NULL; // 用于记录链表的最后一个节点

    // 尝试读取第一行
    if (fgets(buffer, MAX_FILENAME_LENGTH, file)) 
    {
        buffer[strcspn(buffer,"\n")] = 0; // 去除换行符
        *head = create_node(buffer); // 创建头节点
        if (*head == NULL) 
        {
            fclose(file);
            return;
        }
        tail = *head; // 将tail指向头节点
    } 
    else {
        // 如果文件为空，关闭文件并返回
        fclose(file);
        return;
    }

    // 继续读取剩余的行
    while (fgets(buffer, MAX_FILENAME_LENGTH, file)) 
    {
        buffer[strcspn(buffer, "\n")] = 0; // 去除换行符
        Node new_node = create_node(buffer);
        if (new_node == NULL) 
        {
            fprintf(stderr, "内存分配失败。\n");
            continue;
        }
        tail->next = new_node; // 将新节点链接到链表末尾
        tail = new_node; // 更新tail为新节点
    }

    // 形成循环链表
    if (tail != NULL) 
    {
        tail->next = *head;
    }
    fclose(file);
}

// 显示循环链表中的所有图片
void list_pictures(Node head) 
{
    if (head == NULL || head->next == NULL) 
    {
        printf("链表为空。\n");
        return;
    }

    Node current = head; // 从链表的第一个数据节点开始
    do 
    {
        printf("%s\n", current->filename);
        current = current->next;
    } while (current != head); // 循环直到再次到达第一个数据节点
}

  